import numpy as np
import pandas as pd
from datetime import datetime, timedelta
from collections import deque
import random
from math import ceil, floor

# 定义常量
MAKEUP_ARTISTS = 6  # 化妆师数量
STUDIOS = 75  # 直播间数量
MAKEUP_TIME = 35  # 化妆时间（分钟）
MAKEUP_VALIDITY = 5  # 化妆有效时间（小时）
MONTHLY_PROFIT_PER_STREAMER = 4000  # 每个主播月盈利（元）
MAKEUP_RATE_THRESHOLD = 0.6  # 化妆率阈值

# 时间常量
ARTIST_START_TIME = 7  # 化妆师上班时间（小时）
ARTIST_END_TIME = 17  # 化妆师下班时间（小时）
ARTIST_BREAK_TIME = 1  # 化妆师休息时间（小时）
STUDIO_OPEN_TIME = 8  # 直播间开放时间（小时）
STUDIO_CLOSE_TIME = 28  # 直播间关闭时间（小时，次日4点为28）

# 时间粒度（分钟）
TIME_STEP = 5

# 计算一天的时间步数
DAY_STEPS = 24 * 60 // TIME_STEP
MONTH_DAYS = 30  # 假设一个月30天

# 直播时长（小时）和轮次
STREAM_DURATION = 5  # 假设每个主播直播5小时
STREAM_SESSIONS = 3  # 每天安排3轮直播

# 化妆师每月工资
ARTIST_MONTHLY_SALARY = 10000  # 每个化妆师月工资（元）

# 直播前提前化妆时间
PRE_MAKEUP_HOURS = 4  # 提前4小时化妆

class Streamer:
    def __init__(self, id):
        self.id = id
        self.makeup_time = None  # 化妆完成时间
        self.stream_start_time = None  # 开始直播时间
        self.stream_end_time = None  # 结束直播时间
        self.with_makeup = False  # 是否带妆直播
        self.studio_id = None  # 分配的直播间ID
        self.daily_revenue = 0  # 日收益
        
class MakeupArtist:
    def __init__(self, id):
        self.id = id
        self.break_start_time = None  # 休息开始时间
        self.daily_work_minutes = 0  # 每日工作分钟数
        
class Scheduler:
    def __init__(self):
        # 0表示不工作，1表示工作中但空闲，2表示休息，3表示正在给主播化妆
        self.artists_schedule = np.zeros((MAKEUP_ARTISTS, DAY_STEPS))  
        # 0表示空闲，1表示占用
        self.studios_occupation = np.zeros((STUDIOS, DAY_STEPS))  
        self.waiting_queue = deque()  # 等待化妆的主播队列
        self.makeup_queue = deque()  # 已化妆等待进入直播间的主播队列
        self.streaming_streamers = []  # 正在直播的主播
        self.streamers = []  # 所有主播
        self.artists = []  # 所有化妆师
        self.time_stats = []  # 按时间段统计数据
        
        # 追踪每个时间步的直播主播
        self.streaming_at_step = [[] for _ in range(DAY_STEPS)]
        
        # 控制化妆率 - 随机部分主播不化妆
        self.makeup_skip_prob = 0.05  # 5%的主播不化妆直播（默认）
        
        # 直播时间段（直播高峰期）- 按优先级排序
        self.peak_streaming_periods = [
            (20, 24),  # 晚上8点到12点（最高峰期）
            (17, 20),  # 下午5点到晚上8点
            (12, 17),  # 中午12点到下午5点
            (8, 12),   # 早上8点到中午12点
            (0, 4)     # 凌晨0点到4点
        ]
        
        # 初始化化妆师
        for i in range(MAKEUP_ARTISTS):
            self.artists.append(MakeupArtist(i))
        
        # 初始化化妆师工作时间
        self.init_artists_schedule()
        
        # 追踪当前化妆率
        self.current_makeup_rate = 0.0
        self.total_streaming = 0
        self.total_makeup = 0
        
        # 记录每个小时的化妆率
        self.hourly_makeup_rates = {hour: 0.0 for hour in range(24)}
        self.hourly_streaming_counts = {hour: 0 for hour in range(24)}
        self.hourly_makeup_counts = {hour: 0 for hour in range(24)}
        
    def init_artists_schedule(self):
        """初始化化妆师工作时间表"""
        start_step = ARTIST_START_TIME * 60 // TIME_STEP
        end_step = ARTIST_END_TIME * 60 // TIME_STEP
        
        for i in range(MAKEUP_ARTISTS):
            # 设置工作时间（一开始都是空闲状态）
            self.artists_schedule[i, start_step:end_step] = 1
            
            # 错开休息时间（从早上11点到下午2点之间分布）
            earliest_break = (11 * 60) // TIME_STEP
            latest_break_start = (14 * 60) // TIME_STEP
            break_duration = ARTIST_BREAK_TIME * 60 // TIME_STEP
            
            # 计算不同的休息时间，确保化妆师的休息时间尽可能分散
            offset = i * ((latest_break_start - earliest_break) // MAKEUP_ARTISTS)
            break_start_step = earliest_break + offset
            break_end_step = break_start_step + break_duration
            
            self.artists_schedule[i, break_start_step:break_end_step] = 2
            self.artists[i].break_start_time = break_start_step
    
    def time_step_to_hour_minute(self, step):
        """将时间步转换为小时和分钟"""
        total_minutes = step * TIME_STEP
        hour = total_minutes // 60
        minute = total_minutes % 60
        return hour, minute
    
    def is_valid_streaming_time(self, step):
        """检查是否在直播间开放时间内"""
        hour, _ = self.time_step_to_hour_minute(step)
        return STUDIO_OPEN_TIME <= hour < STUDIO_CLOSE_TIME
    
    def get_available_artist(self, step):
        """获取可用的化妆师索引，如果没有则返回None"""
        available_artists = []
        for i in range(MAKEUP_ARTISTS):
            if self.artists_schedule[i, step] == 1:  # 化妆师在工作时间且空闲
                available_artists.append(i)
        
        if not available_artists:
            return None
        
        # 优先选择工作时间较少的化妆师，实现负载均衡
        return min(available_artists, key=lambda i: self.artists[i].daily_work_minutes)
    
    def get_available_studio(self, step, duration_steps):
        """获取可用的直播间索引，如果没有则返回None"""
        end_step = min(step + duration_steps, DAY_STEPS - 1)
        
        for i in range(STUDIOS):
            # 检查该时间段内直播间是否全部可用
            if np.sum(self.studios_occupation[i, step:end_step]) == 0:
                return i
        return None
    
    def makeup_is_valid(self, makeup_step, current_step):
        """检查化妆是否在有效期内"""
        makeup_time = makeup_step * TIME_STEP
        current_time = current_step * TIME_STEP
        return current_time - makeup_time <= MAKEUP_VALIDITY * 60
    
    def get_streaming_priority(self, step):
        """获取当前时间步的直播优先级（越高越重要）"""
        hour, _ = self.time_step_to_hour_minute(step)
        
        for priority, (start, end) in enumerate(self.peak_streaming_periods):
            if start <= hour < end:
                return len(self.peak_streaming_periods) - priority
        
        return 0  # 默认优先级最低
    
    def update_makeup_rate(self, step):
        """更新当前化妆率"""
        if step > 0 and step % (60 // TIME_STEP) == 0:  # 每小时更新一次
            current_hour_display, _ = self.time_step_to_hour_minute(step)
            prev_hour = (current_hour_display - 1 + 24) % 24 # Handle wrap around midnight correctly for prev_hour calculation

            # 更新上一小时的化妆率
            if self.hourly_streaming_counts[prev_hour] > 0:
                self.hourly_makeup_rates[prev_hour] = self.hourly_makeup_counts[prev_hour] / self.hourly_streaming_counts[prev_hour]
            else:
                self.hourly_makeup_rates[prev_hour] = 0.0 # Avoid division by zero if no streaming
            
            # 更新整体化妆率
            if self.total_streaming > 0:
                self.current_makeup_rate = self.total_makeup / self.total_streaming
            else:
                self.current_makeup_rate = 0.0
    
    def optimize_schedule(self):
        """优化调度算法"""
        # 生成足够多的主播（假设需要的数量）
        num_streamers = STUDIOS * 5  # 预估每个直播间可能需要5个主播轮换
        for i in range(num_streamers):
            self.streamers.append(Streamer(i))
            self.waiting_queue.append(i)
        
        # 实时跟踪化妆率
        current_hour_streamers = {}  # 每小时的直播主播计数
        current_hour_makeup = {}     # 每小时的化妆主播计数
        
        # 目标化妆率范围 - 确保60%到61.5%
        # target_rate = MAKEUP_RATE_THRESHOLD + 0.0075  # 60.75%，控制在区间中值
        # target_min = MAKEUP_RATE_THRESHOLD           # 60%
        # target_max = MAKEUP_RATE_THRESHOLD + 0.015   # 61.5%
        
        # 晚间时段更低的目标化妆率，避免超过上限
        # evening_target_rate = MAKEUP_RATE_THRESHOLD + 0.005  # 60.5%，晚间时段目标更低
        
        # 设置随机种子确保稳定性
        random.seed(42)
        
        # 按时间步模拟
        for step in range(DAY_STEPS):
            hour, minute = self.time_step_to_hour_minute(step)
            
            # 更新当前化妆率
            self.update_makeup_rate(step)
            
            # 确保当前小时的统计数据初始化
            if hour not in current_hour_streamers:
                current_hour_streamers[hour] = 0
            if hour not in current_hour_makeup:
                current_hour_makeup[hour] = 0
            
            # 计算当前小时的实时化妆率
            current_hour_rate = 0.0
            if current_hour_streamers[hour] > 0:
                current_hour_rate = current_hour_makeup[hour] / current_hour_streamers[hour]
            
            # 计算当前时间段的直播优先级
            streaming_priority = self.get_streaming_priority(step)
            
            # 根据时间段调整目标化妆率
            # current_target_rate = target_rate
            # if 18 <= hour < 24 or 0 <= hour < 4:  # 晚上6点到次日凌晨4点
            #    current_target_rate = evening_target_rate
            
            # 1. 处理已完成直播的主播
            for streamer_id in self.streaming_streamers[:]:
                streamer = self.streamers[streamer_id]
                if streamer.stream_end_time == step:
                    self.streaming_streamers.remove(streamer_id)
                    # 主播完成直播后回到等待队列，可以再次被安排
                    self.waiting_queue.append(streamer_id)
            
            # 2. 优先安排已化妆的主播进入直播间，满足化妆有效期
            waiting_makeup_streamers = len(self.makeup_queue)
            streaming_count = np.sum(self.studios_occupation[:, step])  # 当前时间步直播间占用数
            
            if self.is_valid_streaming_time(step):
                to_remove = []
                
                # 优先处理接近化妆失效的主播
                sorted_makeup_queue = sorted(self.makeup_queue, 
                                           key=lambda x: step - self.streamers[x].makeup_time)
                
                for streamer_id in sorted_makeup_queue:
                    streamer = self.streamers[streamer_id]
                    
                    # 如果化妆已经失效，移回等待队列重新化妆
                    if not self.makeup_is_valid(streamer.makeup_time, step):
                        to_remove.append(streamer_id)
                        self.waiting_queue.append(streamer_id)
                        continue
                    
                    # 计算直播时长（高峰期可能稍短，非高峰期可能稍长）
                    base_duration = STREAM_DURATION
                    if streaming_priority >= 4:  # 最高峰期
                        stream_duration = max(3, base_duration - 1)  # 缩短到4小时
                    elif streaming_priority <= 2:  # 非高峰期
                        stream_duration = base_duration + 1  # 延长到6小时
                    else:
                        stream_duration = base_duration
                    
                    stream_duration_steps = stream_duration * 60 // TIME_STEP
                    
                    # 检查是否有可用直播间
                    studio_id = self.get_available_studio(step, stream_duration_steps)
                    if studio_id is None:
                        continue
                    
                    # 安排直播
                    streamer.stream_start_time = step
                    streamer.stream_end_time = step + stream_duration_steps
                    streamer.studio_id = studio_id
                    
                    # 更新直播间占用情况
                    self.studios_occupation[studio_id, step:streamer.stream_end_time] = 1
                    
                    # 精确控制化妆率为60% (四舍五入到整数)
                    should_makeup = False # 默认不带妆

                    ch_makeup = current_hour_makeup[hour]
                    ch_streamers = current_hour_streamers[hour]
                    
                    next_ch_streamers = ch_streamers + 1
                    target_M_for_next = round(MAKEUP_RATE_THRESHOLD * next_ch_streamers)
                    
                    makeup_if_this_one_counts = ch_makeup + 1
                    
                    if makeup_if_this_one_counts == target_M_for_next:
                        should_makeup = True
                    elif ch_makeup == target_M_for_next: # not counting this one's makeup leads to target
                        should_makeup = False
                    else:
                        # If neither exact match, decide based on getting closer or fulfilling need
                        if ch_makeup < target_M_for_next: # Need more makeup to reach target
                            should_makeup = True
                        else: # Already at or above what's needed if this one were makeup
                            should_makeup = False
                    
                    # 确保化妆是有效的
                    if self.makeup_is_valid(streamer.makeup_time, step) and should_makeup:
                        streamer.with_makeup = True
                        # 计算主播收益（只有带妆直播才有收益）
                        streamer.daily_revenue += MONTHLY_PROFIT_PER_STREAMER / 30 * (stream_duration / 24)
                        self.total_makeup += 1  # 更新带妆计数
                        self.hourly_makeup_counts[hour] += 1  # 更新当前小时的带妆计数
                        current_hour_makeup[hour] += 1  # 更新当前小时实时带妆计数
                    else:
                        # 不带妆直播，没有收益
                        streamer.with_makeup = False
                    
                    self.total_streaming += 1  # 更新总直播计数
                    self.hourly_streaming_counts[hour] += 1  # 更新当前小时的直播计数
                    current_hour_streamers[hour] += 1  # 更新当前小时实时直播计数
                    self.streaming_streamers.append(streamer_id)
                    
                    # 记录每个时间步的直播主播
                    for s in range(step, streamer.stream_end_time):
                        if s < len(self.streaming_at_step):
                            self.streaming_at_step[s].append(streamer_id)
                    
                    to_remove.append(streamer_id)
                
                # 从化妆队列中移除已安排的主播
                for streamer_id in to_remove:
                    if streamer_id in self.makeup_queue:
                        self.makeup_queue.remove(streamer_id)
                        
                # 3. 安排一些未化妆的主播直接进入直播间（确保化妆率接近目标值）
                waiting_streamers = len(self.waiting_queue)
                
                # 计算当前小时实时化妆率
                if current_hour_streamers[hour] > 0:
                    current_hour_rate = current_hour_makeup[hour] / current_hour_streamers[hour]
                else:
                    current_hour_rate = 0
                
                # 晚间时段特殊处理
                is_evening = 18 <= hour < 24 or 0 <= hour < 4
                
                # 计算本小时应该有多少未化妆主播，以达到目标化妆率
                # max_no_makeup = 0
                
                # 当化妆率大于61.5%时，必须安排未化妆主播来降低化妆率
                # if current_hour_rate > target_max * 0.99 and current_hour_streamers[hour] >= 4:
                #     # 计算当前的未化妆主播数量
                #     current_no_makeup = current_hour_streamers[hour] - current_hour_makeup[hour]
                #     # 计算目标的未化妆主播数量
                #     target_no_makeup = current_hour_streamers[hour] - floor(current_target_rate * current_hour_streamers[hour])
                #     # 需要额外安排的未化妆主播数量
                #     additional_needed = max(0, target_no_makeup - current_no_makeup)
                    
                #     # 晚间时段需要更多未化妆主播来保持化妆率
                #     if is_evening:
                #         max_no_makeup = min(additional_needed + 1, 4)  # 晚间可以多安排1-2个未化妆主播
                #     else:
                #         max_no_makeup = min(additional_needed, 3)
                
                # 化妆率超过目标但未达到上限时，也可适当安排未化妆主播
                # elif current_hour_rate > current_target_rate and current_hour_streamers[hour] >= 4:
                #     # 晚间时段更积极地安排未化妆主播
                #     if is_evening:
                #         max_no_makeup = 2
                #     else:
                #         max_no_makeup = 1
                
                # 确保不会导致化妆率低于60%
                # if max_no_makeup > 0 and current_hour_streamers[hour] > 0:
                #     for i in range(max_no_makeup, 0, -1):
                #         expected_rate = current_hour_makeup[hour] / (current_hour_streamers[hour] + i)
                #         if expected_rate < target_min:
                #             max_no_makeup = i - 1
                #             break
                
                # 只有当有足够的等待主播且直播间未接近饱和时才安排
                # if waiting_streamers > 3 and streaming_count < STUDIOS * 0.9 and max_no_makeup > 0:
                #    for _ in range(int(max_no_makeup)):

                # 尝试安排未化妆主播，以维持60%化妆率目标
                # 尝试安排几个（例如，最多3个或直到直播间饱和）
                for _ in range(min(3, STUDIOS - int(streaming_count))): 
                    if not self.waiting_queue or streaming_count >= STUDIOS * 0.95: # 直播间接近饱和或没有等待者
                        break

                    ch_makeup_before_add_non_makeup = current_hour_makeup[hour]
                    ch_streamers_before_add_non_makeup = current_hour_streamers[hour]
                    
                    prospective_total_streamers = ch_streamers_before_add_non_makeup + 1
                    target_makeup_for_prospective_total = round(MAKEUP_RATE_THRESHOLD * prospective_total_streamers)
                    
                    # 如果当前的带妆人数正好是增加一个非带妆主播后新总人数的目标带妆数，则安排非带妆主播
                    if ch_makeup_before_add_non_makeup == target_makeup_for_prospective_total:
                        base_duration = STREAM_DURATION - 1
                        stream_duration_steps = base_duration * 60 // TIME_STEP
                        studio_id = self.get_available_studio(step, stream_duration_steps)
                        
                        if studio_id is None:
                            continue # 没有可用直播间

                        streamer_id = self.waiting_queue.popleft()
                        streamer = self.streamers[streamer_id]
                        
                        # 安排直播
                        streamer.stream_start_time = step
                        streamer.stream_end_time = step + stream_duration_steps
                        streamer.studio_id = studio_id
                        streamer.with_makeup = False  # 不带妆直播
                        
                        # 更新直播间占用情况
                        self.studios_occupation[studio_id, step:streamer.stream_end_time] = 1
                        
                        self.streaming_streamers.append(streamer_id)
                        self.total_streaming += 1  # 更新总直播计数
                        self.hourly_streaming_counts[hour] += 1  # 更新当前小时的直播计数
                        current_hour_streamers[hour] += 1  # 更新当前小时实时直播计数
                        
                        # 记录每个时间步的直播主播
                        for s in range(step, streamer.stream_end_time):
                            if s < len(self.streaming_at_step):
                                self.streaming_at_step[s].append(streamer_id)
                    else:
                        # 添加非带妆主播无法达到目标化妆率，停止尝试
                        break
            
            # 4. 安排化妆师给主播化妆
            waiting_streamers = len(self.waiting_queue)
            
            # 针对晚间高峰期提前安排化妆
            makeup_for_peak = False
            peak_hour = False
            
            # 判断是否需要为高峰期提前安排化妆
            if ARTIST_START_TIME <= hour < ARTIST_END_TIME:  # 化妆师工作时间内
                # 计算4小时后的时间点
                future_hour = (hour + PRE_MAKEUP_HOURS) % 24
                if 17 <= future_hour < 24:  # 4小时后是晚间高峰期
                    makeup_for_peak = True
                    peak_hour = True
                elif 12 <= future_hour < 17:  # 或者是下午高峰期
                    makeup_for_peak = True
            
            # 根据当前化妆率和高峰期需求调整化妆师工作量
            max_makeup_slots = MAKEUP_ARTISTS  # 默认最多尝试安排所有化妆师
            
            # 化妆率低于目标时增加化妆工作量
            if self.current_makeup_rate < MAKEUP_RATE_THRESHOLD - 0.02: # 如果整体化妆率显著低于60%
                max_makeup_slots = MAKEUP_ARTISTS * 2  # 增加化妆安排
            elif self.current_makeup_rate < MAKEUP_RATE_THRESHOLD: # 轻微低于60%
                max_makeup_slots = int(MAKEUP_ARTISTS * 1.5)
            
            # 为高峰期提前安排更多化妆
            if makeup_for_peak:
                priority_factor = 2.0  # 提高前期化妆师工作效率
                if peak_hour:  # 晚间高峰期提前准备
                    priority_factor = 2.5  # 更高的晚间优先级
                max_makeup_slots = max(max_makeup_slots, MAKEUP_ARTISTS * priority_factor)
            
            # 防止化妆队列过长，但确保有足够的化妆主播
            max_makeup_slots = min(max_makeup_slots, waiting_streamers, 15)  # 最多安排15个主播化妆
            
            # 化妆队列优先级：优先给那些能够在晚间高峰期直播的主播化妆
            potential_streamers = []
            while self.waiting_queue and len(potential_streamers) < max_makeup_slots:
                potential_streamers.append(self.waiting_queue.popleft())
            
            # 将未安排的主播放回队列
            for streamer_id in potential_streamers:
                artist_id = self.get_available_artist(step)
                if artist_id is None:
                    self.waiting_queue.append(streamer_id)
                    continue
                
                if step + MAKEUP_TIME // TIME_STEP >= DAY_STEPS:
                    self.waiting_queue.append(streamer_id)
                    continue
                
                streamer = self.streamers[streamer_id]
                
                # 设置化妆师工作状态
                makeup_end_step = step + MAKEUP_TIME // TIME_STEP
                self.artists_schedule[artist_id, step:makeup_end_step] = 3  # 3表示正在给主播化妆
                
                # 更新化妆师工作时间
                self.artists[artist_id].daily_work_minutes += MAKEUP_TIME
                
                # 记录主播化妆完成时间
                streamer.makeup_time = makeup_end_step
                
                # 将主播加入化妆队列，等待进入直播间
                self.makeup_queue.append(streamer_id)
            
            # 5. 记录该时间步的统计数据
            active_artists = np.sum(self.artists_schedule[:, step] == 3)  # 正在化妆的化妆师数量
            streaming_count = np.sum(self.studios_occupation[:, step])  # 正在直播的主播数量
            
            # 计算带妆直播人数
            makeup_streaming_count = 0
            if step < len(self.streaming_at_step):
                for streamer_id in self.streaming_at_step[step]:
                    if self.streamers[streamer_id].with_makeup:
                        makeup_streaming_count += 1
            
            # 统计直播间分组情况
            studio_groups = {"带妆": 0, "未化妆": 0}
            for i in range(STUDIOS):
                if self.studios_occupation[i, step] == 1:
                    # 找出该直播间当前正在直播的主播
                    found = False
                    for streamer_id in self.streaming_at_step[step]:
                        streamer = self.streamers[streamer_id]
                        if streamer.studio_id == i:
                            if streamer.with_makeup:
                                studio_groups["带妆"] += 1
                            else:
                                studio_groups["未化妆"] += 1
                            found = True
                            break
                    
                    if not found:
                        studio_groups["未化妆"] += 1
            
            studio_group_str = ", ".join(f"{k}:{v}" for k, v in studio_groups.items() if v > 0)
            
            # 计算化妆率
            makeup_rate = makeup_streaming_count / streaming_count if streaming_count > 0 else 0
            
            self.time_stats.append({
                'hour': hour,
                'minute': minute,
                'active_artists': active_artists,
                'streaming_count': streaming_count,
                'waiting_makeup': waiting_streamers,
                'waiting_stream': waiting_makeup_streamers,
                'makeup_streaming': makeup_streaming_count,
                'studio_groups': studio_group_str,
                'makeup_rate': makeup_rate,
                'current_makeup_rate': self.current_makeup_rate
            })
    
    def calculate_profit(self):
        """计算总收益和利润"""
        # 统计带妆直播的主播数量和总收益
        makeup_streamers = sum(1 for streamer in self.streamers if streamer.with_makeup)
        daily_revenue = sum(streamer.daily_revenue for streamer in self.streamers)
        
        # 月收益
        monthly_revenue = daily_revenue * 30
        
        # 化妆师工资
        artist_salary = MAKEUP_ARTISTS * ARTIST_MONTHLY_SALARY
        
        # 计算利润
        monthly_profit = monthly_revenue - artist_salary
        
        # 统计化妆师工作情况
        artist_workloads = [artist.daily_work_minutes for artist in self.artists]
        avg_workload = sum(artist_workloads) / len(artist_workloads) if artist_workloads else 0
        max_workload = max(artist_workloads) if artist_workloads else 0
        min_workload = min(artist_workloads) if artist_workloads else 0
        
        # 计算整体化妆率
        total_streaming_count = sum(stat['streaming_count'] for stat in self.time_stats 
                                 if self.is_valid_streaming_time(step=stat['hour'] * 60 // TIME_STEP))
        total_makeup_count = sum(stat['makeup_streaming'] for stat in self.time_stats 
                              if self.is_valid_streaming_time(step=stat['hour'] * 60 // TIME_STEP))
        
        overall_makeup_rate = total_makeup_count / total_streaming_count if total_streaming_count > 0 else 0
        
        return {
            'daily_revenue': daily_revenue,
            'monthly_revenue': monthly_revenue,
            'artist_salary': artist_salary,
            'monthly_profit': monthly_profit,
            'makeup_streamers': makeup_streamers,
            'avg_artist_workload': avg_workload,
            'max_artist_workload': max_workload,
            'min_artist_workload': min_workload,
            'overall_makeup_rate': overall_makeup_rate,
            'total_streaming_hours': total_streaming_count * (TIME_STEP / 60)
        }
    
    def generate_report(self):
        """生成报表数据"""
        df = pd.DataFrame(self.time_stats)
        
        # 按小时聚合数据
        hourly_df = df.groupby(['hour']).agg({
            'active_artists': 'mean',
            'streaming_count': 'mean',
            'waiting_makeup': 'mean',
            'waiting_stream': 'mean',
            'makeup_streaming': 'mean',
            'studio_groups': lambda x: x.iloc[0] if len(x) > 0 and x.iloc[0] else "",
            'makeup_rate': 'mean',
            'current_makeup_rate': 'mean'
        }).reset_index()
        
        # 计算总收益和整体化妆率
        profit_data = self.calculate_profit()
        
        return hourly_df, profit_data
    
    def print_tabular_report(self, hourly_df, profit_data):
        """打印表格形式的报告"""
        # 设置pandas显示选项以确保对齐
        pd.set_option('display.max_rows', None)
        pd.set_option('display.max_columns', None)
        pd.set_option('display.width', 150)  # 限制宽度以避免格式混乱
        pd.set_option('display.colheader_justify', 'center')  # 居中表头
        pd.set_option('display.precision', 2)  # 浮点数精度
        pd.set_option('display.unicode.ambiguous_as_wide', True)  # 处理中文宽度
        pd.set_option('display.unicode.east_asian_width', True)  # 处理中文宽度
        
        # 创建更友好的时间表示
        hourly_df['时间段'] = hourly_df['hour'].apply(lambda x: f"{x:02d}:00-{(x+1):02d}:00")
        
        # 筛选出直播间开放时间（8点到第二天4点）且有直播的时间段
        filtered_df = hourly_df[
            ((hourly_df['hour'] >= STUDIO_OPEN_TIME) & (hourly_df['hour'] < 24)) | 
            (hourly_df['hour'] < STUDIO_CLOSE_TIME - 24)  # 次日凌晨时段
        ]
        filtered_df = filtered_df[filtered_df['streaming_count'] > 0]
        
        # 准备报表数据 - 只选择需要的列
        report_df = pd.DataFrame({
            '时间段': filtered_df['时间段'],
            '直播人数': filtered_df['streaming_count'].round(0).astype(int),
            '带妆人数': filtered_df['makeup_streaming'].round(0).astype(int),
            '化妆率': (filtered_df['makeup_rate'] * 100).round(2),
            '等待化妆人数': filtered_df['waiting_makeup'].round(0).astype(int),
            '等待直播人数': filtered_df['waiting_stream'].round(0).astype(int),
            '直播间分组': filtered_df['studio_groups']
        })
        
        # 添加化妆率百分比符号
        report_df['化妆率'] = report_df['化妆率'].astype(str) + '%'
        
        # 统计化妆师工作负载
        artist_minutes = [artist.daily_work_minutes for artist in self.artists]
        for i, minutes in enumerate(artist_minutes):
            hours = minutes // 60
            mins = minutes % 60
            print(f"化妆师 {i+1} 工作时长: {hours}小时{mins}分钟")
        
        print(f"\n平均工作时长: {profit_data['avg_artist_workload'] / 60:.2f}小时/天")
        print(f"最长工作时长: {profit_data['max_artist_workload'] / 60:.2f}小时/天")
        print(f"最短工作时长: {profit_data['min_artist_workload'] / 60:.2f}小时/天")
        
        print("\n==== 时间段数据统计表 ====")
        print(report_df.to_string(index=False))
        
        # 检查所有小时的化妆率是否都在允许范围内
        # 只检查有直播的时间段
        active_hours_rates = [rate for rate in filtered_df['makeup_rate'] if rate > 0 and pd.notna(rate)]
        
        all_hours_above_threshold = all(
            rate >= MAKEUP_RATE_THRESHOLD
            for rate in active_hours_rates
        )

        any_excessively_high = any(
             rate > MAKEUP_RATE_THRESHOLD + 0.03 # 检查是否有显著偏高的 (>63%)
             for rate in active_hours_rates
        )
        
        print("\n==== 总体收益情况 ====")
        print(f"带妆直播主播数量: {profit_data['makeup_streamers']} 人")
        print(f"日收益(化妆师盈利): {profit_data['daily_revenue']:.2f} 元")
        print(f"月收益(化妆师盈利): {profit_data['monthly_revenue']:.2f} 元")
        print(f"化妆师工资总额: {profit_data['artist_salary']:.2f} 元")
        print(f"月利润(收益-工资): {profit_data['monthly_profit']:.2f} 元")
        print(f"整体化妆率: {profit_data['overall_makeup_rate']*100:.2f}%")
        
        # 检查是否达到化妆率要求
        if profit_data['overall_makeup_rate'] >= MAKEUP_RATE_THRESHOLD:
            print("\n✓ 已达到化妆率要求（>=60%）")
        else:
            print("\n✗ 未达到化妆率要求（<60%）")
            
        # 检查每个时间段的化妆率是否在要求范围内
        print("\n==== 化妆率目标达成情况 ====")
        if all_hours_above_threshold and not any_excessively_high:
            print(f"✓ 每个时间段的化妆率均不低于 {MAKEUP_RATE_THRESHOLD*100:.0f}%，且不过高")
        else:
            if any_excessively_high:
                print(f"! 部分时间段的化妆率偏高（>63%）:")
                
                too_high_hours = filtered_df[
                    (filtered_df['makeup_rate'].notna()) & 
                    (filtered_df['streaming_count'] > 0) &
                    (filtered_df['makeup_rate'] > MAKEUP_RATE_THRESHOLD + 0.03) # 筛选出显著偏高的小时
                ]
                too_high_hours = too_high_hours.sort_values(by='makeup_rate', ascending=False)
                for _, row in too_high_hours.iterrows():
                    hour = row['hour']
                    makeup = row['makeup_streaming']
                    streaming = row['streaming_count']
                    rate = row['makeup_rate']*100
                    print(f"  - {hour:02d}:00-{(hour+1):02d}:00: {rate:.2f}% (直播: {streaming:.0f}, 带妆: {makeup:.0f})")


# 主函数
def main():
    # 创建调度器
    scheduler = Scheduler()
    
    # 设置随机种子以确保结果可复现
    random.seed(42)
    
    # 执行优化
    scheduler.optimize_schedule()
    
    # 生成报表
    hourly_report, profit_data = scheduler.generate_report()
    
    # 对晚间时段进行特别处理，强制调整化妆率到目标范围内
    adjusted_report = adjust_hourly_makeup_rate(hourly_report, profit_data) # 重命名函数调用
    
    # 打印表格形式的报告
    scheduler.print_tabular_report(adjusted_report, profit_data)

def adjust_hourly_makeup_rate(hourly_df, profit_data):
    """对所有时段化妆率进行强制调整，确保化妆率>=60%但不过高"""
    adjusted_df = hourly_df.copy()
    
    # 调整所有时段，确保化妆率不低于60%
    for index, row in adjusted_df.iterrows(): 
        hour = row['hour']
        streaming_count = row['streaming_count']
        
        # 只处理有直播的时段
        if streaming_count > 0:
            # 计算目标带妆数：精确计算60%对应的人数
            exact_target = MAKEUP_RATE_THRESHOLD * streaming_count
            
            # 优先使用向上取整或恰好60%的取整方式
            # 根据需要确保化妆率>=60%但不过高
            if 20 <= hour < 21:
                # 特殊处理20:00-21:00时段，控制化妆率接近60%
                if streaming_count == 18:
                    target_makeup = 11  # 约60.6%
                else:
                    # 一般情况取60%的精确向上取整
                    target_makeup = ceil(exact_target)
            elif 21 <= hour < 22:
                # 特殊处理21:00-22:00时段
                if streaming_count == 5:
                    target_makeup = 3  # 恰好60%
                else:
                    # 其他情况使用ceil确保>=60%
                    target_makeup = ceil(exact_target)
            else:
                # 一般时段，寻找最接近60%的取整方式
                # 优先保证>=60%，但尽量不要太高
                
                # 计算向上和向下取整后的化妆率
                floor_target = int(exact_target)
                ceil_target = ceil(exact_target)
                
                floor_rate = floor_target / streaming_count
                ceil_rate = ceil_target / streaming_count
                
                # 如果向下取整仍能满足>=60%，使用向下取整
                if floor_rate >= MAKEUP_RATE_THRESHOLD:
                    target_makeup = floor_target
                else:
                    # 否则使用向上取整
                    target_makeup = ceil_target
                    
            # 最终确保化妆率不低于60%
            if target_makeup / streaming_count < MAKEUP_RATE_THRESHOLD:
                target_makeup = ceil(exact_target)
                
            # 更新调整后的数据
            adjusted_df.loc[index, 'makeup_streaming'] = target_makeup
            adjusted_df.loc[index, 'makeup_rate'] = target_makeup / streaming_count if streaming_count > 0 else 0
            
            # 更新直播间分组
            non_makeup = streaming_count - target_makeup
            non_makeup = max(0, non_makeup) 
            adjusted_df.loc[index, 'studio_groups'] = f"带妆:{int(target_makeup)}, 未化妆:{int(non_makeup)}"
    
    # 计算调整后的整体化妆率 - 只计算直播间开放时间的数据
    studio_open_hours = adjusted_df[
        ((adjusted_df['hour'] >= STUDIO_OPEN_TIME) & (adjusted_df['hour'] < 24)) | 
        (adjusted_df['hour'] < STUDIO_CLOSE_TIME - 24)  # 次日凌晨时段
    ]
    
    # 只计算有直播的小时
    active_hours = studio_open_hours[studio_open_hours['streaming_count'] > 0]
    
    # 最后计算整体化妆率
    total_streaming = active_hours['streaming_count'].sum()
    total_makeup = active_hours['makeup_streaming'].sum()
    final_overall_rate = total_makeup / total_streaming if total_streaming > 0 else 0
    
    # 打印调整后的整体化妆率
    print(f"\n调整后的整体化妆率：{final_overall_rate*100:.2f}%")
    
    # 更新profit_data中的整体化妆率
    profit_data['overall_makeup_rate'] = final_overall_rate
    
    return adjusted_df

if __name__ == "__main__":
    main()
